﻿using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Reflection;
using System.Windows.Media.Imaging;

namespace Automator.Tasks.Core
{
    /// <summary>
    /// Base Task class
    /// </summary>
    public partial class Task: INotifyPropertyChanged
    {
        
        #region Fields

        private string _title;
        private string _description;
        private TaskCollection _dependentTasks = null;
        private TaskAttributesCollection _attributes = new TaskAttributesCollection();
        private TaskExecutingState _state = TaskExecutingState.Ready;
        private bool _isActive;

        #endregion

        #region Properties

        /// <summary>
        /// Header of the Task
        /// </summary>
        public string Title
        {
            get { return _title; }
            set { _title = value; OnPropertyChanged("Title"); }
        }
        
        /// <summary>
        /// Brief description, what this Task does
        /// </summary>
        public string Description
        {
            get { return _description; }
            set { _description = value; OnPropertyChanged("Description"); }
        }
        
        /// <summary>
        /// Image of the Task
        /// </summary>
        public BitmapImage TaskImage { get; protected set; }
        
        /// <summary>
        /// Defines whether this Task and its dependencies will be executed
        /// </summary>
        public bool IsActive 
        { 
            get { return _isActive; } 
            set { _isActive = value; OnPropertyChanged("IsActive"); }
        }
        
        /// <summary>
        /// Defines whether IsActive property can be changed by user
        /// </summary>
        public bool IsEnabled { get; protected set; }
        
        /// <summary>
        /// Reference to Task entity which have current Task in its dependencies. Sets automatically when adding a Task to dependency collection
        /// </summary>
        public Task ParentTask { get; set; }
        
        /// <summary>
        /// Defines whether current Task can accept other Tasks to its dependency collection
        /// </summary>
        public bool CanHaveDependencies { get; protected set; }

        public ReadOnlyTaskCollection DependentTasks 
        {
            get { return new ReadOnlyTaskCollection(_dependentTasks); } 
        }
        public TaskAttributesCollection Attributes { get {return _attributes; } }

        public virtual string CommandText
        {
            get { return String.Empty; }
            set { OnPropertyChanged("CommandText"); }
        }

        public TaskExecutingState State
        {
            get { return _state; }
            set { _state = value; OnPropertyChanged("State"); }
        }
        
        public string InputObject { get; protected set; }
        
        public string OutputObject { get; protected set; }
        
        public string AssemblyFileName { get; private set; }
        
        public string AssemblyFileVersion { get; private set; }

        #endregion

        #region Methods

        public Task()
        {            
            IsActive = true;
            IsEnabled = true;
            CanHaveDependencies = true;
            InputObject = "Any";
            OutputObject = "None";
            RecreateDependenciesList();
            
            Assembly assembly = Assembly.GetAssembly(this.GetType());
            AssemblyFileName = Path.GetFileName(assembly.Location);
            AssemblyFileVersion = assembly.GetName().Version.ToString();
        }
        
        private void RecreateDependenciesList()
        {
            _dependentTasks = new TaskCollection();
            _dependentTasks.CollectionChanged += new NotifyCollectionChangedEventHandler(_dependentTasks_CollectionChanged);            
        }

        /// <summary>
        /// If a new dependent Task was added, automatically set current Task entity as its parent 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _dependentTasks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)                
            {
                foreach (object obj in e.NewItems)
                {
                    (obj as Task).ParentTask = this;
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (object obj in e.OldItems)
                {
                    (obj as Task).ParentTask = null;
                }
            }
                
        }
       
        /// <summary>
        /// Adds a new dependency to current Task
        /// </summary>
        /// <param name="value">Task that needs to be added</param>
        /// <exception cref="AddDependencyException">Thrown if CanHaveDependencies property is set to false</exception>
        public void AddDependency(Task value)
        {
            if (this.CanHaveDependencies)
            {
                _dependentTasks.Add(value);
            }
            else
                throw new AddDependencyException();
        }
        
        /// <summary>
        /// Removes a dependency from current Task
        /// </summary>
        /// <param name="value">Task that needs to be removed</param>
        public void RemoveDependency(Task value)
        {
            _dependentTasks.Remove(value);
        }
        
        /// <summary>
        /// Inserts a dependency at specified position
        /// </summary>
        /// <param name="value">Task to be inserted</param>
        /// <param name="index">Index to insert the Task</param>
        public void InsertDependency(Task value, int index)
        {
            _dependentTasks.Insert(index, value);
        }
        
        /// <summary>
        /// Moves the element of the collection
        /// </summary>
        /// <param name="oldIndex"></param>
        /// <param name="newIndex"></param>
        public void MoveDependency(int oldIndex, int newIndex)
        {
            _dependentTasks.Move(oldIndex, newIndex);
        }
        
        /// <summary>
        /// Peforms a edit operation on current Task
        /// </summary>
        public virtual bool EditTask()
        {
            this.State = TaskExecutingState.Ready;
            return true;
        }

        public void ResetState(bool WithDependencies = true)
        {
            this.State = TaskExecutingState.Ready;
            if (WithDependencies)
                foreach (Task task in this.DependentTasks)
                    task.ResetState(WithDependencies);
        }
        
        public Task Clone()
        {          
            Task clone = this.MemberwiseClone() as Task;            
            clone.ParentTask = null;
            clone.RecreateDependenciesList();
            
            clone._attributes = new TaskAttributesCollection();
            foreach (TaskAttributeDefinition attr in this.Attributes)
                clone._attributes.Add(new TaskAttributeDefinition() { Key = attr.Key, Value = attr.Value } );
            
            foreach (Task child in this.DependentTasks)
                clone.AddDependency(child.Clone());
            
            return clone;
        }

        #endregion        

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        #endregion
    }
}
